/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Module for handling a group of connections - header.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 */

#ifndef _CONNECTIONLIST_H_
#define _CONNECTIONLIST_H_

#include "connection.h"
#include "routing.h"
#include "connectionListEntry.h"
#include "idmap.h"
#include <sys/types.h>
#include <pthread.h>
#include <inttypes.h>

// Includes for required features (select() or epoll() mechanism)
#if defined(USE_SELECT_IO) && defined(USE_EPOLL_IO) 
#error Cannot define both: USE_SELECT_IO and USE_EPOLL_IO, choose either one!

#elif defined(USE_SELECT_IO)
//#warning Using select() IO.
#include <sys/select.h>

#elif defined(USE_EPOLL_IO)
//#warning Using epoll() IO.
#include <sys/epoll.h>

#else
# error No IO mechanism defined, must define USE_SELECT_IO or USE_EPOLL_IO.

#endif


#define CONNECTIONLIST_TMP_BUFFERSIZE           8192

#define CONNECTIONLIST_ERROR                    -1
#define CONNECTIONLIST_TARGETNOTFOUND           -2
#define CONNECTIONLIST_LINKERROR                -3

#define CONNECTIONLIST_ERROR_ENTRY              0xFFFFFFFF

#define CONNECTIONLIST_OK                       0
#define CONNECTIONLIST_SEND_DELAYED             1
#define CONNECTIONLIST_SEND_COMPLETE            2

#define CONNECTIONLIST_ID_UNDEFINED             0x00000000
#define CONNECTIONLIST_TARGETID_LOCAL           0x00000000
#define CONNECTIONLIST_SOURCEID_LOCAL           0x00000000
#define CONNECTIONLIST_HUBID_BROADCAST          0xFFFF ///< Broadcast to all HUB:s
#define CONNECTIONLIST_HUBID_LOCAL              0x0000 ///< Local trans. No need to know the local Hub ID.
#define CONNECTIONLIST_CLIENTID_BROADCAST       0xFFFF ///< Broadcast of all clients in HUB
#define CONNECTIONLIST_CLIENTID_LOCAL           0x0000 ///< ClientID for "local" connection (HUB/CLIENT)
#define CONNECTIONLIST_CLIENTID_HUB             CONNECTIONLIST_CLIENTID_LOCAL


#define CONNECTIONLIST_DEFAULT_HUBID            0x00010000

// ID handling macros
#define ID2HUBID(x)                            (((x)>>16)&0xFFFF)
#define ID2CLIENTID(x)                         ((x)&0xFFFF)
#define HUBCLIENT2ID(x,y)                      ((((x)&0xFFFF)<<16)|((y)&0xFFFF))

typedef struct _connectionList
{
  /// Mutex for accessing connectionList
  pthread_mutex_t mutex;

  /// Own information
  uint32_t localHubID; ///< Local hubID. Upper 16 bits contain the HubID, lower bits are zero.
  uint32_t localClientSequence; ///< Current client sequence, only lower 16bits used.

  // idMap for storing the connections.
  TIDMap connectionMap; ///< Map of connection entries, keyed by connectionID.

  // IO handling specific.
#if defined(USE_SELECT_IO)
  // file descriptor set for select()
  fd_set read_set;              ///< select() Read set
  fd_set write_set;             ///< select() Write set
  fd_set exception_set;         ///< select() Exception set
  int highest_set_fd;           ///< Highest file descriptor. If this is Zero, regeneration is required.

#elif defined(USE_EPOLL_IO)
  int epoll_fd;                 ///< epoll file descriptor

#endif

  /// Temporary storage to read data from connection... class (no stack used here)
  unsigned char tmpBuffer[CONNECTIONLIST_TMP_BUFFERSIZE];

} TConnectionList;



/**
 * Creates AND Initializes a connectionList object
 *
 * @param cl             Pointer to mem location to init.
 * @return               Zero on success, negative on error
 */
TConnectionList *connectionListCreate();


/**
 * Initializes a pre-allocated connectionList object
 *
 * @param cl             Pointer to mem location to init.
 * @return               Zero on success, negative on error
 */
int connectionListInit( TConnectionList *cl );


/**
 * De-Initializes a connectionList object
 *
 * @param cl             Pointer to mem location to deinit.
 *
 */
void connectionListDeinit( TConnectionList *cl );



#if defined(USE_SELECT_IO)

/** Select based waiting.
 *
 */
int connectionListDoSelect( TConnectionList *cl,
                            fd_set *readset,
                            fd_set *writeset,
                            fd_set *exceptionset,
                            unsigned int timeout_ms); 

#elif defined(USE_EPOLL_IO)

/** epoll() based waiting.
 *
 */
int connectionListDoEpollWait( TConnectionList *cl,
                               struct epoll_event *events,
                               unsigned int aMaxEvents,
                               unsigned int timeout_ms); 

#endif


/** Register this HUB to HUB specified by given TConnectionListEntry pointer.
 *
 */
int connectionListRegisterHub(TConnectionList *cl,
                              TConnectionListEntry *cle);

TConnectionListEntry *connectionListAddConnection( TConnectionList *cl,
                                                   EConnectionType type,
                                                   EConnectionState state,
                                                   EConnectionProtocol protocol,
                                                   unsigned int flags,
                                                   TConnection *nC);

int connectionListRemoveConnection( TConnectionList *cl,
                                    uint32_t ID );


int connectionListGetConnectionIterator(TConnectionList *cl,
                                        TIDMapEntryIterator *aIter);

TConnectionListEntry *connectionListGetNextConnection(TConnectionList *cl,
                                                      TIDMapEntryIterator *aIter);

void connectionListUpdateEntryFlags(TConnectionList *cl,
                                    TConnectionListEntry *cle,
                                    const unsigned int aNewFlags);

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////




int connectionListReassignConnectionID(TConnectionList *cl,
                                       uint32_t from,
                                       uint32_t to);

int connectionListSetLinkDelayByEntry(TConnectionList *cl, TConnectionListEntry *entry,
                                      const int delay_ms);
int connectionListSetLinkDelay(TConnectionList *cl, const int link_id,
                               const int delay_ms);

uint32_t connectionListGetLocalHubID(const TConnectionList *cl);

int connectionListReceiveData( TConnectionList *cl,
                               TConnectionListEntry *e);

int connectionListFlushOutputBuffer( TConnectionList *cl,
                                     TConnectionListEntry *target);

int connectionListSendRawDataByEntry( TConnectionList *cl,
                                      const char *input, int count,
                                      TConnectionListEntry *target );

int connectionListSendDataByEntry( TConnectionList *cl,
                                   TBuffer *input, int from, int to,
                                   TConnectionListEntry *target );

int connectionListSendRawDataByID( TConnectionList *cl,
                                   const char *input, int count,
                                   uint32_t target );

int connectionListSendDataByID( TConnectionList *cl,
                                TBuffer *input, int from, int count,
                                uint32_t target);

void connectionListSetLocalHubID(TConnectionList *cl, const uint32_t hubID);

int connectionListGetOutputBufferSizeByID(TConnectionList *cl, uint32_t target);

int connectionListGetGatewayIDbyTargetID(TConnectionList *cl,
                                         const uint32_t target,
                                         uint32_t *gateway);

TConnectionListEntry *connectionListGetConnectionEntryByID(TConnectionList *cl,
                                                           uint32_t ID);
#endif

